package com.cnap.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.cnap.config.ScheduleConfig;
import com.cnap.db.entity.IntelligentHistoryPO;
import com.cnap.db.entity.IntelligentTaskPO;
import com.cnap.db.entity.MicroServicePO;
import com.cnap.db.entity.MsgCenterInfoPO;
import com.cnap.db.mapper.IntelligentHistoryMapper;
import com.cnap.db.mapper.IntelligentTaskMapper;
import com.cnap.db.mapper.MicroServiceMapper;
import com.cnap.db.mapper.MsgCenterInfoMapper;
import com.cnap.model.intelligent.IntelligentClusterInfo;
import com.cnap.model.intelligent.IntelligentHistoryVO;
import com.cnap.model.intelligent.IntelligentResultEnum;
import com.cnap.model.intelligent.IntelligentTask;
import com.cnap.model.intelligent.UpdateIntelligentDTO;
import com.cnap.model.schedule.engine.DeployPolicyCalcResV2;
import com.cnap.service.IntelligentScheduleService;
import com.cnap.service.StrategyService;
import com.cnap.utils.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.stream.Collectors;

/**
 * <类文件描述>
 *
 * @author xc
 **/
@Service
public class IntelligentScheduleServiceImpl implements IntelligentScheduleService {
    private static final Logger LOGGER = LoggerFactory.getLogger(IntelligentScheduleServiceImpl.class);

    private static final Map<String, ScheduledFuture<?>> CACHE_MAP = new ConcurrentHashMap<>();
    // 保存迁移线程
    private static final Map<String, ScheduledFuture<?>> MIGRATE_MAP = new ConcurrentHashMap<>();

    private final ThreadPoolTaskScheduler threadPoolTaskScheduler;
    private final ScheduleConfig scheduleConfig;
    private final IntelligentTaskMapper taskMapper;
    private final IntelligentHistoryMapper historyMapper;
    private final MsgCenterInfoMapper msgCenterInfoMapper;
    private final MicroServiceMapper microServiceMapper;

    @Autowired
    public IntelligentScheduleServiceImpl(ThreadPoolTaskScheduler threadPoolTaskScheduler, ScheduleConfig scheduleConfig,
                                          IntelligentTaskMapper intelligentTaskMapper, IntelligentHistoryMapper intelligentHistoryMapper,
                                          MsgCenterInfoMapper msgCenterInfoMapper, MicroServiceMapper microServiceMapper) {
        this.threadPoolTaskScheduler = threadPoolTaskScheduler;
        this.scheduleConfig = scheduleConfig;
        this.taskMapper = intelligentTaskMapper;
        this.historyMapper = intelligentHistoryMapper;
        this.msgCenterInfoMapper = msgCenterInfoMapper;
        this.microServiceMapper = microServiceMapper;
    }

    @PostConstruct
    public void init() {
        if (scheduleConfig.isActive()) {
            List<IntelligentTaskPO> allPos = taskMapper.selectAll();
            allPos.stream().filter(IntelligentTaskPO::isActive).map(IntelligentTaskPO::toTask).forEach(task -> {
                ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(task, new CronTrigger(task.getCron()));
                CACHE_MAP.put(task.getSvcId(), future);
                LOGGER.info("[intelligent-schedule]: init service({}), cron={}", task.getSvcId(), task.getCron());
            });
        }
    }

    @Override
    public void updateNewSuggestCluster(String svcId, List<IntelligentClusterInfo> suggest) {
        historyMapper.updateSuggestClusterAndStatusBySvcIdWithNewest(svcId, suggest);
    }

    @Override
    public void addNewIntelligentSchedule(String svcId, int duration, int active) {
        // 不执行，直接返回
        if (active == 0) {
            LOGGER.warn("[intelligent-schedule]: service({}) active={}, just return", svcId, active);
            return;
        }
        IntelligentTaskPO oldTask = taskMapper.selectBySvcId(svcId);
        if (Objects.nonNull(oldTask)) {
            LOGGER.warn("[intelligent-schedule]: service({}) has added intelligent schedule task, delete and reInsert", svcId);
            unActiveIntelligentTask(svcId, duration);
            taskMapper.deleteBySvcId(svcId);
        }
        IntelligentTaskPO po = new IntelligentTaskPO();
        String cron = StrFormatter.format(scheduleConfig.getCron(), duration);
        po.setCron(cron);
        po.setServiceId(svcId);
        po.setCreateTime(LocalDateTime.now(ZoneId.of("Asia/Shanghai")));
        po.setActive(true);
        taskMapper.insertTask(po);

        LOGGER.info("[intelligent-schedule]: add service({}) success", svcId);
        // 实际线程启用

        IntelligentTask task = po.toTask();
        ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(task, new CronTrigger(task.getCron()));
        CACHE_MAP.put(svcId, future);
    }

    @Override
    public void updateIntelligent(UpdateIntelligentDTO updateIntelligentDTO) {
        if (updateIntelligentDTO.getActive() > 0) {
            // active
            activeIntelligentTask(updateIntelligentDTO.getSvcId(), updateIntelligentDTO.getDuration());
        } else {
            // unActive
            unActiveIntelligentTask(updateIntelligentDTO.getSvcId(), updateIntelligentDTO.getDuration());
        }
        // 发生更新，就需要删除待迁移线程
        // 如果此时该微服务准备迁移，则需要同步删除
        deleteMigrateTask(updateIntelligentDTO.getSvcId());
    }

    private void deleteMigrateTask(String svcId) {
        ScheduledFuture<?> future = MIGRATE_MAP.remove(svcId);
        if (Objects.nonNull(future)) {
            future.cancel(true);
            LOGGER.warn("[intelligent-migrate]: stop migrate service({}) success by unActive", svcId);
        }
    }

    @Override
    public List<IntelligentHistoryVO> getHistoryVOBySvcId(String svcId) {
        List<IntelligentHistoryPO> pos = historyMapper.selectBySvcId(svcId);
        return pos.stream().map(IntelligentHistoryPO::toVO).collect(Collectors.toList());
    }

    @Override
    public void deleteHistoryById(String id) {
        IntelligentHistoryPO po = historyMapper.selectById(id);
        historyMapper.deleteById(id);
        // 如果此微服务准备迁移，则同步删除
        deleteMigrateTask(po.getServiceId());
    }

    @Override
    public void saveIntelligentHistory(String svcId, List<IntelligentClusterInfo> currentInfos, List<IntelligentClusterInfo> suggestInfos, boolean needMigrate, DeployPolicyCalcResV2 deployRes) {
        // 1. 插入历史记录
        IntelligentHistoryPO historyPO = saveHistory(svcId, currentInfos, suggestInfos);
        LOGGER.info("[intelligent-task]: save history, svcId={}, need migrate={}", svcId, needMigrate);
        if (needMigrate) {
            // 2. 启动迁移定时任务
            // 迁移执行的时间（history记录产生的当天开始+1天）
            Date migrateDate = Date.from(historyPO.getActiveTime().atZone(ZoneId.of("Asia/Shanghai")).toInstant());
            ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(() -> {
                StrategyService strategyService = SpringUtil.getBean(StrategyService.class);
                strategyService.migrateService(svcId, suggestInfos, deployRes);
            }, migrateDate);

            MIGRATE_MAP.put(svcId, future);
            LOGGER.info("[intelligent-task]: add migrate task, svcId={}, current={}, migrate={}", svcId, JsonUtil.objectToJsonStr(currentInfos), JsonUtil.objectToJsonStr(suggestInfos));
        }

        // 保存消息
        MicroServicePO svcPO = microServiceMapper.selectMicroServiceById(svcId);
        MsgCenterInfoPO po = new MsgCenterInfoPO();
        po.setMsgType("调度优化更新");
        po.setUserName(svcPO.getUserName());
        po.setItem01(svcPO.getMicroServiceName());

        if (Objects.isNull(suggestInfos) || suggestInfos.isEmpty()) {
            // 为空，说明无需优化
            po.setItem02(IntelligentResultEnum.NO_OPTIMIZATION_REQUIRED.getMsg());
        } else {
            // 初始为待优化，等到一天后真正执行迁移时更改为已优化
            po.setItem02(IntelligentResultEnum.TO_BE_OPTIMIZED.getMsg());
        }

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        po.setItem03(historyPO.getActiveTime().format(formatter));
        po.setCreateTime(LocalDateTime.now(ZoneId.of("Asia/Shanghai")));

        msgCenterInfoMapper.insert(po);
    }

    @Override
    public void deleteIntelligentInfoBySvcId(String svcId) {
        // 需要删除历史记录、任务记录、任务线程、实际迁移线程
        // 删除任务记录
        taskMapper.deleteBySvcId(svcId);
        // 删除历史记录
        historyMapper.deleteBySvcId(svcId);
        // 删除任务线程
        ScheduledFuture<?> future = CACHE_MAP.remove(svcId);
        if (Objects.nonNull(future)) {
            future.cancel(true);
            LOGGER.info("[intelligent-schedule]: stop service({}) by delete svc", svcId);
        }
        // 删除迁移线程
        ScheduledFuture<?> future1 = MIGRATE_MAP.remove(svcId);
        if (Objects.nonNull(future1)) {
            future1.cancel(true);
            LOGGER.warn("[intelligent-migrate]: stop migrate service({}) success by delete svc", svcId);
        }
    }

    private IntelligentHistoryPO saveHistory(String svcId, List<IntelligentClusterInfo> currentInfos, List<IntelligentClusterInfo> suggestInfos) {
        IntelligentHistoryPO po = new IntelligentHistoryPO();
        LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        // 使用当前天的开始
        po.setCreateTime(LocalDateTimeUtil.beginOfDay(now));
        po.setServiceId(svcId);
        if (Objects.isNull(suggestInfos) || suggestInfos.isEmpty()) {
            // 为空，说明无需优化
            po.setStatus(IntelligentResultEnum.NO_OPTIMIZATION_REQUIRED.getStatus());
            po.setSuggestClusters(Collections.emptyList());
        } else {
            // 初始为待优化，等到一天后真正执行迁移时更改为已优化
            po.setStatus(IntelligentResultEnum.TO_BE_OPTIMIZED.getStatus());
        }
        po.setActiveTime(now.plusDays(1));

        po.setCurrentClusters(currentInfos);
        po.setSuggestClusters(suggestInfos);
        po.setId(IdUtil.simpleUUID());

        historyMapper.insertHistory(po);
        return po;
    }

    private void activeIntelligentTask(String svcId, int duration) {
        IntelligentTaskPO oldTask = taskMapper.selectBySvcId(svcId);
        if (Objects.isNull(oldTask)) {
            LOGGER.error("[intelligent-schedule]: service({}) has no intelligent task config", svcId);
            return;
        }
        if (oldTask.isActive()) {
            LOGGER.warn("[intelligent-schedule]: service({}) is active, remove and reStart", svcId);
            // 停用之前的线程（如果有）
            ScheduledFuture<?> old = CACHE_MAP.remove(svcId);
            if (Objects.nonNull(old)) {
                old.cancel(true);
                LOGGER.info("[intelligent-schedule]: stop service({}) by active", svcId);
            }
        }
        String cron = StrFormatter.format(scheduleConfig.getCron(), duration);
        taskMapper.updateActiveBySvcId(svcId, 1, cron);
        // 实际线程启用
        IntelligentTask task = oldTask.toTask();
        task.setCron(cron);
        ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(task, new CronTrigger(cron));
        CACHE_MAP.put(svcId, future);

        LOGGER.info("[intelligent-schedule] active service({}) success", svcId);
    }

    private void unActiveIntelligentTask(String svcId, int duration) {
        IntelligentTaskPO oldTask = taskMapper.selectBySvcId(svcId);
        if (Objects.isNull(oldTask)) {
            LOGGER.error("[intelligent-schedule]: service({}) has no intelligent task config", svcId);
            return;
        }
        if (!oldTask.isActive()) {
            LOGGER.warn("[intelligent-schedule]: service({}) is unActive, just return", svcId);
            return;
        }
        String cron = StrFormatter.format(scheduleConfig.getCron(), duration);
        taskMapper.updateActiveBySvcId(svcId, 0, cron);
        // 实际线程启用

        ScheduledFuture<?> future = CACHE_MAP.remove(svcId);
        if (Objects.nonNull(future)) {
            future.cancel(true);
            LOGGER.info("[intelligent-schedule]: stop service({}) by unActive", svcId);
        }
        LOGGER.info("[intelligent-schedule]: unActive service({}) success", svcId);
    }
}
